package org.dromara.system.controller.system;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.convert.Convert;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.UserConstants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.dromara.system.domain.bo.SysDeptBo;
import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.service.ISysDeptService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;


/**
 * @Author 刘武贵
 * @Date 2024/8/15 22:00
 * @Description 功能描述：部门信息
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/system/dept")
public class SysDeptController extends BaseController {

    /**
     * 部门管理 服务层
     */
    private final ISysDeptService deptService;

    /**
     * 获取部门列表
     * 说明：本方法通过调用deptService的selectDeptList方法，实现根据提供的查询条件（dept对象）
     * 获取部门列表，并使用R对象封装查询结果，最后返回一个表示操作成功并携带部门列表数据的响应结果
     *
     * @param dept 查询条件，包含部门相关信息，用于过滤查询结果
     * @return 返回封装了部门数据列表的响应结果对象R，列表包含SysDeptVo类型的部门视图对象
     */
    @SaCheckPermission("system:dept:list")
    @GetMapping("/list")
    public R<List<SysDeptVo>> list(SysDeptBo dept) {
        List<SysDeptVo> depts = deptService.selectDeptList(dept);
        return R.ok(depts);
    }

    /**
     * 查询部门列表（排除节点）
     * 此方法的目的是在返回部门树结构时，排除掉指定部门ID及其所有子部门，
     * 以实现例如角色授权时选择部门的场景中，避免选中自身及子部门的需求
     *
     * @param deptId 部门ID，用于排除自身及子部门
     * @return R<List<SysDeptVo>> 封装的部门列表数据
     */
    @SaCheckPermission("system:dept:list")
    @GetMapping("/list/exclude/{deptId}")
    public R<List<SysDeptVo>> excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
        // 查询所有部门信息
        List<SysDeptVo> depts = deptService.selectDeptList(new SysDeptBo());
        // 移除指定部门ID及其所有子部门
        depts.removeIf(d -> d.getDeptId().equals(deptId)
            || StringUtils.splitList(d.getAncestors()).contains(Convert.toStr(deptId)));
        return R.ok(depts);
    }

    /**
     * 根据部门编号获取详细信息
     * 该方法首先检查当前用户是否有权限访问指定的部门数据，
     * 然后调用deptService.selectDeptById(deptId)方法来查询并返回指定部门的详细信息
     *
     * @param deptId 部门ID
     * @return 返回封装了部门信息的响应结果对象R，其中数据部分为SysDeptVo类型
     */
    @SaCheckPermission("system:dept:query")
    @GetMapping(value = "/{deptId}")
    public R<SysDeptVo> getInfo(@PathVariable Long deptId) {
        // 检查用户的数据权限范围，确保用户有权查看请求的部门信息
        deptService.checkDeptDataScope(deptId);
        // 返回部门信息，需确保查询到的部门信息符合当前用户的数据权限
        return R.ok(deptService.selectDeptById(deptId));
    }

    /**
     * 新增部门
     * 该方法在尝试添加新部门之前，首先检查部门名称是否唯一，以避免重复名称的部门被添加到系统中
     * 如果部门名称已存在，则返回失败信息，说明新增失败；否则，尝试插入该部门信息，并返回操作结果
     * 这里的注释详细解释了方法的目的、参数和返回值，以及其核心逻辑：检查唯一性、插入数据、返回结果
     *
     * @param dept 经过验证的部门信息对象，确保数据符合业务要求
     * @return R<Void> 类型的结果对象，其中不包含数据实体，仅表示操作是否成功
     */
    @SaCheckPermission("system:dept:add")
    @Log(title = "部门管理", businessType = BusinessType.INSERT)
    @PostMapping
    public R<Void> add(@Validated @RequestBody SysDeptBo dept) {
        // 检查部门名称是否唯一，防止重复添加
        if (!deptService.checkDeptNameUnique(dept)) {
            return R.fail("新增部门'" + dept.getDeptName() + "'失败，部门名称已存在");
        }
        return toAjax(deptService.insertDept(dept));
    }

    /**
     * 修改部门
     *
     * 该方法用于修改系统中的部门信息，确保待修改的部门名称唯一，且上级部门不为自己，并处理部门状态下的相关约束
     * 如果部门名称已存在或上级部门是自己，或者部门状态为停用但存在未停用的子部门或已分配的用户，则修改失败并返回相应错误信息
     * 否则，调用服务层方法更新部门信息，并返回成功标识
     *
     * @param dept 经过校验的部门信息对象
     * @return R<Void> 返回修改操作结果
     */
    @SaCheckPermission("system:dept:edit")
    @Log(title = "部门管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public R<Void> edit(@Validated @RequestBody SysDeptBo dept) {
        Long deptId = dept.getDeptId();
        // 校验部门是否有数据权限
        deptService.checkDeptDataScope(deptId);
        // 校验部门名称是否唯一
        if (!deptService.checkDeptNameUnique(dept)) {
            return R.fail("修改部门'" + dept.getDeptName() + "'失败，部门名称已存在");
            // 如果上级部门是自己
        } else if (dept.getParentId().equals(deptId)) {
            return R.fail("修改部门'" + dept.getDeptName() + "'失败，上级部门不能是自己");
            // 如果包含停用状态的部门
        } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus())) {
            if (deptService.selectNormalChildrenDeptById(deptId) > 0) {
                return R.fail("该部门包含未停用的子部门!");
                // 查询部门是否存在用户
            } else if (deptService.checkDeptExistUser(deptId)) {
                return R.fail("该部门下存在已分配用户，不能禁用!");
            }
        }
        return toAjax(deptService.updateDept(dept));
    }

    /**
     * 删除部门
     *
     * @param deptId 部门ID
     * @return R<Void> 删除操作的结果
     */
    @SaCheckPermission("system:dept:remove")
    @Log(title = "部门管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{deptId}")
    public R<Void> remove(@PathVariable Long deptId) {
        // 检查当前部门下是否存在下级部门，如果存在则不允许删除
        if (deptService.hasChildByDeptId(deptId)) {
            return R.warn("存在下级部门,不允许删除");
        }
        // 检查当前部门是否存在用户，如果存在则不允许删除
        if (deptService.checkDeptExistUser(deptId)) {
            return R.warn("部门存在用户,不允许删除");
        }
        // 检查当前部门的数据权限
        deptService.checkDeptDataScope(deptId);
        // 执行删除部门操作并返回结果
        return toAjax(deptService.deleteDeptById(deptId));
    }

    /**
     * 获取部门选择框列表
     * 根据部门ID数组查询部门信息
     * 此方法解释了如何根据一组部门ID获取详细的部门信息列表它说明了参数的使用和返回值的意义
     *
     * @param deptIds 部门ID串
     * @return 返回一个R对象，其中包含查询到的部门信息列表如果查询成功，R对象中的data字段将包含查询结果
     */
    @SaCheckPermission("system:dept:query")
    @GetMapping("/optionselect")
    public R<List<SysDeptVo>> optionselect(@RequestParam(required = false) Long[] deptIds) {
        return R.ok(deptService.selectDeptByIds(deptIds == null ? null : List.of(deptIds)));
    }

}
